package bma.common.langutil.concurrent;

import java.util.concurrent.ScheduledFuture;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 
 * @author guanzhong
 * @since 1.0 (imported from BMA.ESP Project)
 *
 */
public abstract class AbstractTimeoutRunnable implements Runnable {

	final Logger log = LoggerFactory.getLogger(AbstractTimeoutRunnable.class);

	protected ScheduledFuture<?> future;

	public ScheduledFuture<?> getFuture() {
		return future;
	}

	public void setFuture(ScheduledFuture<?> future) {
		this.future = future;
	}

	public abstract boolean isValid();

	public abstract long nextDelay(long now);

	public abstract ScheduledFuture<?> newSchedule(long delay);

	public abstract void handleTimeout();
	
	public abstract void setActiveTime(long now);

	@Override
	public void run() {
		checkTimeout(false);
	}

	public void open(long delay) {
		if (future == null) {
			future = newSchedule(delay);
		}
		setActiveTime(System.currentTimeMillis());
	}

	public void close() {
		ScheduledFuture<?> f = future;
		future = null;
		if (f != null) {
			f.cancel(false);
		}
	}

	public boolean checkTimeout(boolean cancel) {
		try {
			if (log.isDebugEnabled()) {
				log.debug("{} checkTimeout({})", this, cancel);
			}
			ScheduledFuture<?> f = future;
			if (cancel && f != null) {
				f.cancel(false);
			}

			if (!isValid()) {
				return false;
			}

			long currentTime = System.currentTimeMillis();
			long nextDelay = nextDelay(currentTime);
			if (log.isDebugEnabled()) {
				log.debug("{} wait {}", this, nextDelay);
			}
			if (nextDelay <= 0) {
				future = null;
				handleTimeout();
				return true;
			} else {
				future = newSchedule(nextDelay);
				return false;
			}
		} catch (Throwable t) {
			log.error(this + " check timeout fail", t);
			return true;
		}
	}

}
